package de.pixel83.piecemaster.ai;

import java.util.ArrayList;

import de.pixel83.piecemaster.Board;
import de.pixel83.piecemaster.Move;
import de.pixel83.piecemaster.Square;
import de.pixel83.piecemaster.Board.MoveResult;

public class MasterAi3 implements GeneticAi {
	private int p = 100;
	private int n = 300;
	private int b = 350; // eindeutig erhöhen
	private int r = 500;
	private int q = 1000;
	private int k = 10000;
	private int pinc = 10;

	private DNA dna = null;

	public String getName() {
		return "MasterAiMichi";
	}

	public MasterAi3(DNA dna) {
		setDNA(dna);
	}

	private void setDNA(DNA dna) {
		this.dna = dna;
		p = dna.get("pawn");
		n = dna.get("knight");
		b = dna.get("bishop");
		r = dna.get("rook");
		q = dna.get("queen");
		k = dna.get("king");
		pinc = dna.get("pawn-increase");
	}

	public MasterAi3() {
		DNA defaultDNA = new DNA(
				"bishop=350;queen=1000;rook=500;king=100000;pawn=100;knight=200;pawn-increase=10");
		setDNA(defaultDNA);
	}

	private Move m0; // best move so far
	private int checktime; // check if its time to check the time

	public Move draw(Board board, char color) {
		checktime = 1000;
		int maxTime = 7 * 1000; // Max time for a draw
		int maxDepth = 100; // Maximum depth
		Move finalMove = null;

		ArrayList<Move> legalMoves = new ArrayList<Move>();
		ArrayList<Square> ownPieces = getAllPieces(board, color);
		for (Square square : ownPieces) {
			legalMoves.addAll(board.getLegalMoves(square));
		}
		long starttime = System.currentTimeMillis();
		for (int depth = 0; depth < maxDepth; depth++) {
			// System.out.println("Running at depth " + (i + 1));
			int a0 = Integer.MIN_VALUE + 2;
			int bestRating = Integer.MIN_VALUE;
			Move bestMove = null;
			for (Move mov : legalMoves) {
				int value = -negaMax(board, mov, depth,
						(Integer.MIN_VALUE + 2), -a0, starttime, maxTime);
				if (value == (Integer.MIN_VALUE)) {
					System.out.println("TIMED OUT AFTER "
							+ (System.currentTimeMillis() - starttime) + " ms");
					return finalMove;
				}
				if (value > bestRating) {
					bestRating = value;
					bestMove = mov;
				}
				if (value > a0)
					a0 = value;
			}
			System.out.println("##### After depth " + (depth + 1) + " move "
					+ bestMove + " was chosen.");
			finalMove = bestMove;
		}
		// System.out.println("#################### Final rating: " +
		// finalRating);

		return finalMove;
	}

	private int negaMax(Board inBoard, Move move, int depth, int a, int b,
			long starttime, int maxTime) {
		if (checktime > 0)
			checktime--;
		else {
			if (isTimedOut(maxTime, starttime)) {
				checktime = 1000;
				return (Integer.MIN_VALUE);
			}
		}

		Board board = new Board(inBoard.toString());
		Board.MoveResult movRes = board.move(move);
		if (movRes != Board.MoveResult.OK) {
			switch (movRes) {
			case DRAW:
				return 0;
			case WIN_W:
				return Integer.MAX_VALUE + 2;
			case WIN_B:
				return Integer.MAX_VALUE + 2;
			case ILLEGAL:
				return Integer.MIN_VALUE + 2;
			}
			return rateBoard(board, board.getColor());
		}

		if (depth <= 0) {
			return rateBoard(board, board.getColor());
		}
		ArrayList<Move> legalMoves = new ArrayList<Move>();
		ArrayList<Square> ownPieces = getAllPieces(board, board.getColor());
		for (Square square : ownPieces) {
			legalMoves.addAll(board.getLegalMoves(square));
		}
		if (legalMoves.size() == 0)
			return Integer.MIN_VALUE + 2;

		int v = Integer.MIN_VALUE + 2;
		Board tempBoard = null;
		for (Move mov : legalMoves) {
			// System.out.println("Moving " + mov.toString());
			tempBoard = new Board(board.toString());

			int value = -negaMax(tempBoard, mov, depth - 1, -b, -a, starttime,
					maxTime);
			if (value == (Integer.MIN_VALUE) && isTimedOut(maxTime, starttime))
				return (Integer.MIN_VALUE);
			if (value > v)
				v = value;
			if (v > a)
				a = v;
			if (v >= b)
				return v;
		}

		return v;
	}

	private boolean isTimedOut(int maxTime, long starttime) {
		return maxTime < System.currentTimeMillis() - starttime;
	}

	private void logRating(int a, int b, int depth) {
		String text = "#################### ";
		for (int i = 0; i < depth; i++) {
			text += "#";
		}
		text += " A=" + a + " B=" + b;
		System.out.println(text);
	}

	private ArrayList<Square> getAllPieces(Board board, char color) {
		ArrayList<Square> list = new ArrayList<Square>();
		for (int r = 0; r < 6; r++) {
			for (int c = 0; c < 5; c++) {
				if (board.getColor(board.getPieceAt(r, c)) == color) {
					list.add(new Square(c, r));
				}
			}
		}
		return list;
	}

	private int rateBoard(Board board) {
		return rateBoard(board, 'W');
	}

	private int rateBoard(Board board, char color) {
		int rating = 0;

		for (int r = 0; r < 6; r++) {
			for (int c = 0; c < 5; c++) {
				rating += getValue(board, r, c, color);
			}
		}
		return rating;
	}

	private int getValue(Board board, int row, int col, char color) {
		int value = 0;
		char piece = board.getPieceAt(row, col);
		char piececolor = board.getColor(piece);
		int factor = (piececolor == color ? 1 : -1);
		piece = Character.toLowerCase(piece);
		switch (piece) {
		case 'p':
			value = p;
			// Pawns closer to promotion are more valuable
			if (piececolor == 'W')
				value += (pinc * (row - 1));
			if (piececolor == 'B')
				value += (pinc * (4 - row));
			break;
		case 'n':
			value = n;
			break;
		case 'b':
			value = b;
			break;
		case 'r':
			value = r;
			break;
		case 'q':
			value = q;
			break;
		case 'k':
			value = k;
			break;
		}
		value = value * factor;
		return value;
	}

	/**
	 * Tests the class RandomAi
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		Board board = new Board();
		Ai ai = new NotSoRandomAi();
		for (int i = 0; i < 3; i++)
			System.out.println(ai.draw(board, 'W'));
	}

	public DNA getDNA() {
		return this.dna;
	}

	public GeneticAi generateAi(DNA dna) {
		return new MasterAi3();
	}

}
